# vim: et ts=4 sts=4 sw=4 tw=0

# ==== 定义影响编译和链接默认行为的 CMake 构建策略
#
# 将 JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION 字符串设置为能成功构建的最新 CMake 版本策略。
# 通过将 JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION 设置为大于最旧策略的值，
# 所有介于 JSONCPP_OLDEST_VALIDATED_POLICIES_VERSION 和本次构建使用的 CMAKE_VERSION 之间的策略
# 都将被设置为新行为，从而抑制与这些策略相关的警告。
#
# 对于使用大于 JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION 策略的 CMake 版本，
# 仍会生成策略警告 "CMake Warning (dev)...Policy CMP0XXX is not set:"
#

# 设置能成功构建的最旧 CMake 版本策略
set(JSONCPP_OLDEST_VALIDATED_POLICIES_VERSION "3.8.0")
# 设置能成功构建的最新 CMake 版本策略
set(JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION "3.13.2")
# 指定项目所需的最小 CMake 版本
cmake_minimum_required(VERSION ${JSONCPP_OLDEST_VALIDATED_POLICIES_VERSION})

# 根据当前 CMake 版本选择合适的策略版本
if("${CMAKE_VERSION}" VERSION_LESS "${JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION}")
    # 使用当前 CMake 版本的策略
    set(JSONCPP_CMAKE_POLICY_VERSION "${CMAKE_VERSION}")
else()
    # 使用已验证的最新策略版本
    set(JSONCPP_CMAKE_POLICY_VERSION "${JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION}")
endif()

# 设置 CMake 策略版本
cmake_policy(VERSION ${JSONCPP_CMAKE_POLICY_VERSION})

# 若支持 CMP0091 策略，则将其设置为新行为
if(POLICY CMP0091)
    cmake_policy(SET CMP0091 NEW)
endif()

#
# 现在列举比 JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION 更新的特定策略，
# 这些策略可能需要单独设置为 NEW 或 OLD
#
# 目前没有需要设置为 NEW 的新策略
foreach(pnew "") 
    if(POLICY ${pnew})
        cmake_policy(SET ${pnew} NEW)
    endif()
endforeach()

# 目前没有需要设置为 OLD 的策略
foreach(pold "") 
    if(POLICY ${pold})
        cmake_policy(SET ${pold} OLD)
    endif()
endforeach()

# 使用 C++11 标准构建库，不受其他可能使用不同 CXX_STANDARD 或 CMAKE_CXX_STANDARD 的软件影响
set(CMAKE_CXX_STANDARD 11)
# 不使用编译器特定的 C++ 扩展
set(CMAKE_CXX_EXTENSIONS OFF)
# 要求必须使用指定的 C++ 标准
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 确保单配置生成器指定了 CMAKE_BUILD_TYPE 的值
if(NOT DEFINED CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE Release CACHE STRING
        "选择构建类型，可选值为: None Debug Release RelWithDebInfo MinSizeRel Coverage.")
endif()

# 将项目的 cmake 目录添加到 CMake 模块搜索路径
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

# 定义项目信息
project(jsoncpp
        # 注意：发布版本时，版本号必须在四个地方更新。
        # 这个繁琐的过程确保 amalgamate、CMake 和 meson 都能报告正确的版本。
        # 1. ./meson.build
        # 2. ./include/json/version.h
        # 3. ./CMakeLists.txt
        # 4. ./MODULE.bazel
        # 重要提示：同时也要更新 PROJECT_SOVERSION！
        VERSION 1.9.7 # <主版本号>[.<次版本号>[.<补丁版本号>[.<微调版本号>]]]
        LANGUAGES CXX)

# 输出 JsonCpp 的版本信息
message(STATUS "JsonCpp Version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
# 设置项目的共享库版本号
set(PROJECT_SOVERSION 27)

# 包含在源码目录构建的 CMake 脚本
include(${CMAKE_CURRENT_SOURCE_DIR}/include/PreventInSourceBuilds.cmake)
# 包含在构建目录安装的 CMake 脚本
include(${CMAKE_CURRENT_SOURCE_DIR}/include/PreventInBuildInstalls.cmake)

# 编译并（针对 jsoncpp_check）运行 JsonCpp 测试可执行文件的选项
option(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" ON)
# 构建后自动运行单元测试的选项
option(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
# 若出现警告则强制编译失败的选项
option(JSONCPP_WITH_WARNING_AS_ERROR "Force compilation to fail if a warning occurs" OFF)
# 报告严格 ISO C 和 ISO C++ 要求的所有警告的选项
option(JSONCPP_WITH_STRICT_ISO "Issue all the warnings demanded by strict ISO C and ISO C++" ON)
# 生成并安装 .pc 文件的选项
option(JSONCPP_WITH_PKGCONFIG_SUPPORT "Generate and install .pc files" ON)
# 生成并安装 CMake 包文件的选项
option(JSONCPP_WITH_CMAKE_PACKAGE "Generate and install cmake package files" ON)
# 编译 JsonCpp 示例的选项
option(JSONCPP_WITH_EXAMPLE "Compile JsonCpp example" OFF)
# 使用静态（MT/MTd）Windows 运行时的选项
option(JSONCPP_STATIC_WINDOWS_RUNTIME "Use static (MT/MTd) Windows runtime" OFF)
# 将 jsoncpp_lib 构建为共享库的选项
option(BUILD_SHARED_LIBS "Build jsoncpp_lib as a shared library." ON)
# 将 jsoncpp_lib 构建为静态库的选项
option(BUILD_STATIC_LIBS "Build jsoncpp_lib as a static library." ON)
# 将 jsoncpp_lib 构建为对象库的选项
option(BUILD_OBJECT_LIBS "Build jsoncpp_lib as a object library." ON)

# 遵循 GNU 文件系统安装约定
include(GNUInstallDirs)

# 如果当前源码目录是项目根目录，则设置输出目录
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" CACHE PATH "Archive output dir.")
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" CACHE PATH "Library output dir.")
    set(CMAKE_PDB_OUTPUT_DIRECTORY     "${CMAKE_BINARY_DIR}/bin" CACHE PATH "PDB (MSVC debug symbol)output dir.")
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" CACHE PATH "Executable/dll output dir.")
endif()

# 检查系统是否存在 memset_s 函数
include(CheckFunctionExists)
check_function_exists(memset_s HAVE_MEMSET_S)
# 若存在 memset_s 函数，则定义宏 HAVE_MEMSET_S
if(HAVE_MEMSET_S)
    add_definitions("-DHAVE_MEMSET_S=1")
endif()

# 若启用了 JSONCPP_USE_SECURE_MEMORY，则定义宏 JSONCPP_USE_SECURE_MEMORY
if(JSONCPP_USE_SECURE_MEMORY)
    add_definitions("-DJSONCPP_USE_SECURE_MEMORY=1")
endif()

# 配置版本文件
configure_file("${PROJECT_SOURCE_DIR}/version.in"
    "${PROJECT_BINARY_DIR}/version"
    NEWLINE_STYLE UNIX)

# 定义将编译警告视为错误的宏
macro(use_compilation_warning_as_error)
    if(MSVC)
        # 仅在调试配置中启用，因为旧版本的 VS STL 在发布配置下编译会产生警告
        add_compile_options($<$<CONFIG:Debug>:/WX>)
    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        # 将警告视为错误
        add_compile_options(-Werror)
        if(JSONCPP_WITH_STRICT_ISO)
            # 发出严格 ISO C++ 要求的所有警告并将其视为错误
            add_compile_options(-pedantic-errors)
        endif()
    endif()
endmacro()

# 包含项目的配置头文件目录
include_directories(${jsoncpp_SOURCE_DIR}/include)

if(MSVC)
    # 仅在调试配置中启用，因为旧版本的 VS STL 在发布配置下编译会产生不可达代码警告
    add_compile_options($<$<CONFIG:Debug>:/W4>)
    if (JSONCPP_STATIC_WINDOWS_RUNTIME)
        # 使用静态 Windows 运行时
        set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
    endif()
endif()

if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    # 使用常规 Clang 或 AppleClang 编译器
    add_compile_options(-Wall -Wconversion -Wshadow)

    if(JSONCPP_WITH_WARNING_AS_ERROR)
        # 将类型转换和符号比较警告视为错误
        add_compile_options(-Werror=conversion -Werror=sign-compare)
    endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    # 使用 GCC 编译器
    add_compile_options(-Wall -Wconversion -Wshadow -Wextra)
    # 暂不启用 -Wsign-conversion

    if(JSONCPP_WITH_STRICT_ISO)
        # 发出严格 ISO C++ 要求的所有警告
        add_compile_options(-Wpedantic)
    endif()
    if(JSONCPP_WITH_WARNING_AS_ERROR)
        # 将类型转换警告视为错误
        add_compile_options(-Werror=conversion)
    endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
    # 使用 Intel 编译器
    add_compile_options(-Wall -Wconversion -Wshadow -Wextra)

    if(JSONCPP_WITH_WARNING_AS_ERROR)
        # 将类型转换警告视为错误
        add_compile_options(-Werror=conversion)
    elseif(JSONCPP_WITH_STRICT_ISO)
        # 发出严格 ISO C++ 要求的所有警告
        add_compile_options(-Wpedantic)
    endif()
endif()

# 若启用了将警告视为错误的选项，则调用相应宏
if(JSONCPP_WITH_WARNING_AS_ERROR)
    use_compilation_warning_as_error()
endif()

# 若启用了生成并安装 .pc 文件的选项
if(JSONCPP_WITH_PKGCONFIG_SUPPORT)
    include(JoinPaths)

    # 拼接 pkg-config 文件所需的库目录路径
    join_paths(libdir_for_pc_file "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}")
    # 拼接 pkg-config 文件所需的包含目录路径
    join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")

    # 配置 pkg-config 文件
    configure_file(
        "pkg-config/jsoncpp.pc.in"
        "pkg-config/jsoncpp.pc"
        @ONLY)
    # 安装生成的 pkg-config 文件
    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkg-config/jsoncpp.pc"
        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
endif()

# 若启用了生成并安装 CMake 包文件的选项
if(JSONCPP_WITH_CMAKE_PACKAGE)
    include(CMakePackageConfigHelpers)
    # 安装导出的目标
    install(EXPORT jsoncpp
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp
        FILE        jsoncpp-targets.cmake)
    # 配置 CMake 包配置文件
    configure_package_config_file(jsoncppConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfig.cmake
        INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp)

    # 编写基本的包版本文件
    write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake"
        VERSION ${PROJECT_VERSION}
        COMPATIBILITY SameMajorVersion)
    # 安装生成的 CMake 包文件
    install(FILES
        ${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake ${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfig.cmake
        ${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp-namespaced-targets.cmake
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp)
endif()

# 若启用了编译并运行测试的选项
if(JSONCPP_WITH_TESTS)
    # 启用测试功能
    enable_testing()
    include(CTest)
endif()

# 构建不同的应用程序
add_subdirectory(src)

# 安装头文件
add_subdirectory(include)

# 若启用了编译示例的选项
if(JSONCPP_WITH_EXAMPLE)
    add_subdirectory(example)
endif()
